{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 安装类库\n",
    "# !mkdir /home/aistudio/external-libraries\n",
    "# !pip install imgaug -t /home/aistudio/external-libraries\n",
    "import sys\n",
    "sys.path.append('/home/aistudio/external-libraries')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image shape: (32, 32, 3)\n",
      "label value: horse\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADFCAYAAAARxr1AAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAHC1JREFUeJztnXuMXHd1x79nZu689732erO24zixnZg8XGEgFNTybAOqFKhaBH9U+SMCKoFUBP9EVGqp1EpUKqD+UVEFNSKVKIEWUFIaKCGiStNAHiTEeZgkdmJnba937X3NzO687+kfMw47+z17Pdldr3fN+UjW7h7fufd378yZe89bVBWO49jELvcCHGcz4wriOBG4gjhOBK4gjhOBK4jjROAK4jgRuII4TgSuII4TwZoURERuE5GXROSYiNy1XotynM2CrDaSLiJxAC8D+CCAUwCeBPAJVX1xpdfEg4QGqaBDlslkaLtYTEhWqVRIptowjxOG/Pp4LE6yIJkgWT7XY+2RJLXFKskaTd5OY/b1jRtfTf2D20iWyeR4NSHvs1xeNGQLJEun+XpjhY9Ao9kkWRAEJEum0vYO6DB8IDWurSrLwpBlANA0rnmjXiNZPN75/k9PnkepUOQPyjL4E9I9bwdwTFVfBQARuQ/A7QBWVJAgFWDnjXs7ZDfd9BbaLpNJkuyV40dJVq/NmseplPj1OeODv2v3MMnecevvkUxCvuCnnjlOsvOzJZI1svxaAMjz5x5//PE/J9lbbnonycqLvM8jzz9FsueeY9nBA3y9LYUDgNm5Asm2je4k2dV795FMhL8B6k3+UqmDv/iqTVbshcWyucbSLMunz46TrL833/H3333+S+b+lrOWR6wxAEtXcqot60BEPiUiT4nIU806fyM5zmbmkhvpqnq3qh5W1cPxgB9zHGczs5ZHrNMAdi35e2dbtiIahqhXOm+JszPnaLvkju0kGx1h2bmpunmcuXN8i85m2V5ZWJwh2anTL5NsbIRujEinUyRTnSfZ4NCAuca3HtrPxxkdJVnTeJ6ePc/X7OWjL5FsboYfkRYW+HGqt99eY98wPwdWQ76O52fPkyyMZ0kmYtkLbDtVq/yoWq7aj6qLNd5nkOZ1yzL756LGR5u13EGeBLBPRK4RkSSAjwN4YA37c5xNx6rvIKraEJHPAvhvAHEA96jqC+u2MsfZBKzlEQuq+iCAB9dpLY6z6fBIuuNEsKY7yJslnohjaLivQ1avsh87YQTXioU5klWMeAAADA8MsWw4T7KbbmGfPowg5dnJUyTLBmykZ7MchGs07DWm0xyrSSX42DHl2EEixobywQPXkWz3VVeRLNfDwcggawVHgcoiOx1qjSLJ5ubZ2TGzwO+X5XAIhM9lscD7q4Z2iKBoxA8b81Mkyy6Lt1SNwLOF30EcJwJXEMeJwBXEcSJwBXGcCDbUSE+nUrju2j0dsiDJ6SepFMvm56dJ1qzb2bxqJBcOD/WT7PoDe0k2PcsJkM8+8wqvMdhBsr7+XpIVY2ysAnZUOQZedyrBstERjhTnMntI9sJznBXQk+HM2zDJDgcAiNVYnkuwsZxK8vdsocTnVyxwhoOEbCxXipwBsNiwsyZmjIzjsMjv4WK9MzrfWOGzsxy/gzhOBK4gjhOBK4jjROAK4jgRbKyRnk7hwP5rO2S5HEef6w023IoFjo42a3Z0dWqCX79jhKsHBw2jenqanQGJBDsNjII5hIYhGQ/s76Aw5G1j4KyCVJIj6VYFYCPNsljIr80aRvZ8jVPOAaA4z8ZyT9pwLoR8jqkGy3LCH7eZeY7MWxF3GE4NAIgpX8dymdPlK4VOw90qJzb339VWjvNbiiuI40TgCuI4EbiCOE4EriCOE8GavFgicgJAEUATQENVD19ke6SWNWtLpXgJVg+rgzccIFk6sJd/8jinGoyNctOHIMGvtxrMZY30jHiTv1uGhvpIFvbY3pJUiutBmk32vtSqRhO8uLFuw0OUCtjzk0/yeqbO83EB4PzZsyyrsmercJZ7dZQWjfUY1/HE+DGS7drDdSw92zlVCADiFfZ4FYxGIPW5ztqWZqO7VJP1cPO+V1W5rYXjXAH4I5bjRLBWBVEAPxGRX4rIp6wNlnZWXCjZASnH2ays9RHr3ap6WkS2A3hIRH6tqo8s3UBV7wZwNwCM7R71mdPOlmKtbX9Ot39OicgP0Gpo/chK28cTCfQNdqZ81BucXlGscPrBVbvYcOtL2c0GqsUTJBscYCMvFuMbaCbLxvPAIDd86AlZtnPn1SQLM7YxmO8zajCMdn+lBW6cEDcaSzRqnLJjpchUjQ6FzzzxS3ONP3/61ySrL/B7M33mdX5xjGtWtg9xus/4aTbSc3l+X7btto30nGFsx4x6oHBZR8hupxqs+hFLRHIi0nPhdwB/AOD51e7PcTYja7mDjAD4gYhc2M+/qeqP12VVjrNJWEvr0VcB3LKOa3GcTYe7eR0ngg2tB4kl4sgNDXbIzkycoO3OzXPccWgHt+iPiz36K2M0Ieg3WvxncmyQDxl1I8UFrqvIGEb60DAfI8jbl1gDNi6TaXY61OtsFFtdAWtlNtwLJa6VeOKJX5DsJw/afpXZOTZ2G4aRX2uywRsPeLuRPv4+Tjf5PTh/hiP4e27gJhkA0GvU2+SM0XoVGvV2iY10x/ltwBXEcSJwBXGcCFxBHCeCDTXSIYBkOtOtYxk2LhNGNHtukY1VNYxDAIgbowV6BzgSn+/laO9rpzhV/twkp4hnhQ3gA9ewkT02yvMNAWCxyeeTSPB6YtY45TpHyE+f4pEBD/2EDfJnn+Xo+My0nSOXTnH6ftMYV2AFpStlw7mwwGn1mRjPXS+e59eePn7GXOPYGI9zyKZ4PuJcrHOf1phqC7+DOE4EriCOE4EriONE4AriOBFsbCQ9rgjyncbtjt2DtF3/do5Sq5XWXLaN9FyMU6Mnpzmye+Qod2t87DFOSD53miP7KWXjcnGGL+d7/9AeLbDnwC6SBXHeJ2KcLTB+jlPgf/xfPyfZIz/jNPZa0xgjYJwLANTrvG29wVkFVaM8oVbj2vXZWXYu5AM+v1jduLbG+wcAsxk+djLJRnoq0/mZEqPUwcLvII4TgSuI40TgCuI4EbiCOE4EFzXSReQeAH8EYEpVb2zLBgF8B8AeACcAfExVOQS9jFAbqDQ6N0ulOXocpA0jK8HGbio0irgBLM5wJP34S9xM7MXnx0k2foKN0ECHSDY7x4byo2d/xdsVuOYeAMb2cvp2b5bPJybsiDh69FWSPf5/L5Bs0QiQJ1NsFDdWyEioGvMDq1WeM1iu8BzGWIJfO7c4SbIwxSMotvezAyMd8GcCAAaG2MlTNcZQ9IWd6e4Jo2mgRTd3kG8CuG2Z7C4AD6vqPgAPt/92nCuOiypIu43P8kSf2wHc2/79XgAfWed1Oc6mYLU2yIiqTrR/P4tWAweTpY3jinN8e3aczcyajXRtNRhasX5RVe9W1cOqerinn+0Nx9nMrDaSPikio6o6ISKjADgkbdBo1HFuqrMT+LZhTkNv1NlYLRr914KQI7MAcPQZnjOICjd1G+jlVPT5XjbSk0Y0e7ZiNG9juxTT5+xU8mdffJRkxblTJDO/wYzIdyLOqfZ9vbzuUDl1X41GawBQq7ODoW7Jmvxk8NZ33UiyXaOjJHv28SMkKzU5Cl+Hvcbd+/k9jBkTA0bLne/rI//5U3N/tK+utmIeAHBH+/c7ANy/yv04zqbmogoiIt8G8HMAB0TklIjcCeDLAD4oIq8A+ED7b8e54rjoI5aqfmKF/3r/Oq/FcTYdHkl3nAg2NN292WiiNNtpgMUavIRGg0eHzc5w5Lo4YxtuR37BUfMbr+URbHWjIdz0JDct6+/lZnK5PHdTLysb5PkcR3oBIDHN9dnlEn9fNRt8jrkcHzubZsM9k+GMgmKRzzkmfL0BIJ1mI79mOFD6hrk84a3vexvJ9h+4hmQDu7jZ3q+feY1khYadqCF5jppXxYjilzqj/U3tbgSb30EcJwJXEMeJwBXEcSJwBXGcCFxBHCeCjfVi1ZuYmez0JhSmOU2hYTRomJpkz1S5YKeaTIyzx6t6/imShVVOISsWjayZkNMzrtrOnq258+zFajT4tQDQ28OvX+znGpGFItdaiPG2NZrs7Yon2OMUGKMhEgl7jERvH3vB4lPcJGH3wb0ku/7wQZJl8vx+Hfr9m0m2fYzTj8oLdqJrWYzmEMZcx3OLndexERrNKwz8DuI4EbiCOE4EriCOE4EriONEsKFGepAMMDq2s0MWj/MSyiVOw0iBDbyzdTbQAKBhGHQnTj7N60kY8+16uYlAIsWGdt0YX1AqcR1KKrXfXOPeEa5PqRsNERp1TgOxjO+E0RwxNL7+8oOcFjKQ5RECAJBO8XGu2suG++9+6ADJ9ozy6ISGsmGsed5fj5XGU7WbX4QBf1Z6E7zPhnTuM2FcQwu/gzhOBK4gjhOBK4jjROAK4jgRrLaz4pcAfBLAhfD2F1X1wYvtK5VO49r9+zr3H2PjOy28rJRRsjD+GnfqA4AnHzLqCco8w8/q+t8/xMZlT54N26mJCZLVa5wB0GzatRbWPnft3E0y6xtMwcauGDUdxQWOwvf0sAE8Mmwb6YFxfXbdzI0Xxq7jcRNiRPaTcX6v40k+iHXNYkn7OlaMcQ75OGdIxJcdO2E4hyxW21kRAL6mqofa/y6qHI6zFVltZ0XH+a1gLTbIZ0XkiIjcIyJcN9lmaWfFwqx3VnS2FqtVkK8DuBbAIQATAL6y0oZLOyv2DnhnRWdrsapIuqq+YR2LyDcA/LCb1zWbTRSLnZHPhGGklSps4KWNsPDcrB1Jr1aM2Xp1Y8ahFUnPGI0KFrgJwNkz3NxBjZl+J4+fNNdYNjoz5rOcij62g9PiFRzZr1Q51f7ka6+QLJviYyyk7Sfo/DB/oSX7uIPjbJmPrWU2qhPGDMbAMNyThndAQnuOYgzGWITQyD6QZe9/d4H01d1B2u1GL/BRADz50nGuALpx834bwHsADIvIKQB/DeA9InIIrabVJwB8+hKu0XEuG6vtrPgvl2AtjrPp8Ei640SwoenuoSrK1U6DN6yyAZxPGnP0jLrwhUU7BbpaZWO5abzeqhefL7DBGoYcmZ2b5/mGFcNYDWPcyRAAEkZkuGGk2sfFqivnt21+jjsPnp/kaH82zt+J9Yp9Hfvi7L3fBc4ACMt8fZqGoZywMgCMqHnGSFdvjaFhmgHLJcay2LKMDVFPd3ecNeMK4jgRuII4TgSuII4TwYYa6QAgyyKnWaPFfjpgIy2jrMuJuDGLEECzueJM0Q7qxmiB6WmOkI/tYsP0lrdxczOJs+F3Zpwb3gHAyVNPkiyTYaO4boyCSKf4mtWNKH61xpkGxTmOpDcCY7gigL4UR6nDODs2qg2+3g3DSA/rfL1jhpFeEz6X8qK9Rg34mieTnDWRS3duZzleLPwO4jgRuII4TgSuII4TgSuI40SwoUa6GpH0uhqpyYb9FMTZcIdRzw4AYjSZswKxahiSuTwbwB/90w+QbO8NbKTHU7zGI0+9YK7xpz96lGTnjbmFzTobxc2YUfsOI3KdYdlChQ337UOcUg8AB27m5na9/Wy4F6rcRM+aAVirsaFdN0obrBT4utHxHwDCqpXaztkL1WWN4pordN1fjt9BHCcCVxDHicAVxHEicAVxnAi6qSjcBeBfAYygVUF4t6r+o4gMAvgOgD1oVRV+TFXtae9tFECITmu50WTjS40UbyvwWavahlutyoafsUuI8fWwbz+PE9t/kLuXZ4b50tWUDb/D736Hucb9+99CsvEzZ0h25ixH9qF8bA35BH90/0Mkm3iZR8wNjnCaPQBs38kN4RZKPN4sbPJ5xwI2nkWMN9H4BBaMLvdN4xgAkBI26GOGR6Za7vysrGckvQHgC6p6EMCtAD4jIgcB3AXgYVXdB+Dh9t+Oc0XRTeO4CVV9uv17EcBRAGMAbgdwb3uzewF85FIt0nEuF2/KBhGRPQB+B8DjAEZU9ULJ2lm0HsGs17zROG5hnivuHGcz07WCiEgewPcAfE5VO6JN2qqHNB/qljaOy/UZPYwcZxPTlYKISICWcnxLVb/fFk9e6I/V/mkMGHecrU03XixBq83PUVX96pL/egDAHQC+3P55fxf7QrC8a55RQ9E00k+MjAvMTdu9fms1ozmA0azAcm3tNobYZ1PcYXBhntNCakZjiOQKLfwG8lz70bOfuxaODA2SzPLAGGUjeOx/HiPZuOE17OmxuxbmjIYIzRKncRiTF8y0m5rR/TFjdVFM8zWrGesGgIRx4mJ8fpZ7T1d44OH9d7HNuwD8GYDnRORXbdkX0VKM74rInQBOAvhYV0d0nC1EN43jHsXKnUzfv77LcZzNhUfSHScCVxDHiWBjmzaoUlqC1cp/ocx6qyHXaVTmbUMrtOYCxvgpMWs0Jbj6Kp7BF6vwGmWBj50MOe0hZRW3AAgCdiRk49wcIhXjc6k0uKaj1GDjOWk4NpJxbtpwwzVj5hr3DhqpJkleY8nocDlvzGusFK2RCMZ7ZaSkiJUXBHPSAerG3MJmo3ON4QqdGpfjdxDHicAVxHEicAVxnAhcQRwngg1v2tCsdBqTGhhjCWrGDL4iyyZP210L1Yg0G+US6O/rI9mQIZs7a3RwDPnSpRNs9DcadkfAWNwwqo13I6izAVwpcwaBGk0S1Oh4GBhNKQa28zkDQDrNC4oLG/mG/wN9yhHyoQbLJie43qVhOBwq4QpNGwzj3TL8q6Vl74OVemDgdxDHicAVxHEicAVxnAhcQRwngg010mMKpJbZ2rGAo89loxnDzAQb5Oen7BIUK7PS6heQy7JRvVBkA/i1l06QLBnj1w71cYfChDEvDwDCpJFKXj1PMjWM/KqVX57hlPy48NubyhkNH8RuiLBY4JR+VTbSYaXQxzMkS6d5jWEPOwikOMfHLdtGdWG58Q0gnTQyMYqdn7NY02cUOs6acQVxnAhcQRwnAlcQx4lgLZ0VvwTgkwAuWM9fVNUHo/YVgyAbdkZTa2WOmqLE6cqL57ijX21hhSi1YaaLUX8exI3uiGXepxjp94UK11c3Cmzs9vWyYQoAkjVq8RdneJ81Pk49yfXe8STXs4sR7e/NGPMf67YBXJ/itHoFG+lqpOTPhvwe1ow5ijEjWp8O2cCPG+cMAIkGX8d4jR0g8WrnumPanZHejRfrQmfFp0WkB8AvReRCT8uvqeo/dHUkx9mCdFOTPgFgov17UUQudFZ0nCuetXRWBIDPisgREblHRLiPDTo7K5YK3lnR2VqspbPi1wFcC+AQWneYr1ivW9pZMd/rnRWdrUVXkXSrs6KqTi75/28A+OFFd9QEYsuCs0GCI+m94LrnoMLR1WqJU8EBIB5jvQ+SbFymAjb80sZ2PSlOES812blQmOdZfcV5eyJEIsavv26Uo8qZPF+LQoGj/fNnOKtgbpaN7IEcG8ADwjIAyC4YWQ7GTMHQmIVYXp4yAaBujJRU4/2PG3XziRWy061ov3FpkdTO91pW7GS1bF8X22ClzooX2o62+SiA57s6ouNsIdbSWfETInIILdfvCQCfviQrdJzLyFo6K0bGPBznSsAj6Y4TwcamuyOGnHYanaEx1y+RZG/X7iFeal/uFfM4QYIN46TRRTxhDKxfWGCjr1nj6HqQYMO2Z4Cj2aFRXw0AiwuGgyHPRnoixw6CsMIW6+lxNtKnZ9mxMTy8m2SBER0HgGST5ZUqn0+xzBkAlV6+tknj+sTT/L7US+wIaNTt62hlJKiRYLE84N5d2zi/gzhOJK4gjhOBK4jjROAK4jgRbLiRntJOAzxmdD/XGhtuuYANvH3X7jOP0zQisePjp0lWKnDk+9grx3h/Rl14Is7G8+g2zuHsMYxsANi2nUer1ZJs+JeEjV1N83YNI50/leXtakaWQV3sVHKNG6n6Rnc7aXBkv17kKH4s4NcGxrp7jcyFecNRAgDxPl57o2o0I5xflmpvHNfC7yCOE4EriONE4AriOBG4gjhOBK4gjhPBxs4oBKDxi8f800b6wZ69O0nWv2ObeYx3LrLH49H/fYxkrx1nj1WtZgyhr3KzgWrIqQ+v13i7dMZO45DcdSTLp4zzMboEJvvZO9U7xJ6fwSE+9sIie5ymZ20P0eCeHSQLA15PptpLsqDGXqJKhWVVNeYJZvlDsWCMdwCAsuGMSuc5VWm5E1SsmQ0GfgdxnAhcQRwnAlcQx4mgm5LbtIg8ISLPisgLIvI3bfk1IvK4iBwTke+IrBCOdZwtTDdGehXA+1S11G7e8KiI/AjA59FqHHefiPwzgDvR6nSyIqqKeqPT2LLqNNIpNi6DGG+X7eGGBgCwQ1nvt/fdRrLx1zn9pDfPKS1Tr79Gsvl5rrUI8mw8V0N7tEDd8E5UjHmEGuO3qFTiFJmEkbJz4/XXkKxvgJ0dg/08tgEApit8nB1Xs+FeOM0Oi8lZHuVQqLOhXTMcMlpnAzrM2N/lqQR/VqZnuT7l9MlTHX9XalxzYnHRO4i2uFBFFLT/KYD3AfiPtvxeAB/p6oiOs4XoygYRkXi7YcMUgIcAHAcwp78ZrXoKK3RbXNo4rlBiF6PjbGa6UhBVbarqIQA7AbwdwPXdHmBp47jevN3I2XE2K2/Ki6WqcwB+BuCdAPpF3pjxtRMAP9A7zhanm/EH2wDUVXVORDIAPgjg79FSlD8BcB+AOwDcf9GjqSBe7zxkLMFLiBk1CzWjaD9pl1pAjOHyO4c4Sj02OEKywjyPWehL8P5mZqdJVrIaCxj1EwAQGl9NWmFDu2p0MkwbcwL7R7gZw/49fKOvG0a/Vm2DNd/LDot8H1/06gzXrFTBEfIzU5MkKxmG++AY18oEeTbmASAEX/OUUQ/UM9DZOjpuNOyw6MaLNQrgXhGJo3XH+a6q/lBEXgRwn4j8LYBn0Oq+6DhXFN00jjuCVkf35fJX0bJHHOeKxSPpjhOBK4jjRCCq3faYW4eDiZwDcBLAMAAOtW5N/Fw2Jxc7l6tV1a6XWMKGKsgbBxV5SlUPb/iBLwF+LpuT9ToXf8RynAhcQRwngsulIHdfpuNeCvxcNifrci6XxQZxnK2CP2I5TgSuII4TwYYriIjcJiIvtUt179ro468FEblHRKZE5PklskEReUhEXmn/HIjax2ZBRHaJyM9E5MV2KfVftOVb7nwuZVn4hipIO+HxnwB8CMBBtCblHtzINayRbwJYXrt7F4CHVXUfgIfbf28FGgC+oKoHAdwK4DPt92Irns+FsvBbABwCcJuI3IpW1vnXVPU6ALNolYW/KTb6DvJ2AMdU9VVVraGVKn/7Bq9h1ajqIwCWFzzfjlbJMbCFSo9VdUJVn27/XgRwFK2q0C13PpeyLHyjFWQMwPiSv1cs1d1CjKjqRPv3swC4yGSTIyJ70MrYfhxb9HzWUhYehRvp64i2fOZbym8uInkA3wPwOVXtmHqzlc5nLWXhUWy0gpwGsGvJ31dCqe6kiIwCQPsnz2PepLTbOH0PwLdU9ftt8ZY9H2D9y8I3WkGeBLCv7V1IAvg4gAc2eA3rzQNolRwD3ZYebwJERNCqAj2qql9d8l9b7nxEZJuI9Ld/v1AWfhS/KQsHVnsuqrqh/wB8GMDLaD0j/uVGH3+Na/82gAkAdbSeae8EMISWt+cVAD8FMHi519nlubwbrcenIwB+1f734a14PgBuRqvs+wiA5wH8VVu+F8ATAI4B+HcAqTe7b081cZwI3Eh3nAhcQRwnAlcQx4nAFcRxInAFcZwIXEEcJwJXEMeJ4P8BDOlRG0/6AGEAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 读取数据\n",
    "reader = paddle.batch(\n",
    "    paddle.dataset.cifar.train10(),\n",
    "    batch_size=8) # 数据集读取器\n",
    "data = next(reader()) # 读取数据\n",
    "index = 4 # 批次索引\n",
    "\n",
    "# 读取图像\n",
    "image = np.array([x[0] for x in data]).astype(np.float32) # 读取图像数据，数据类型为float32\n",
    "image = image * 255 # 从[0,1]转换到[0,255]\n",
    "image = image[index].reshape((3, 32, 32)).transpose((1, 2, 0)).astype(np.uint8) # 数据格式从CHW转换为HWC，数据类型转换为uint8\n",
    "print('image shape:', image.shape)\n",
    "\n",
    "# 图像增强\n",
    "# sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "# seq = iaa.Sequential([\n",
    "#     sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "#     iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "# image = seq(image=image)\n",
    "\n",
    "# 读取标签\n",
    "label = np.array([x[1] for x in data]).astype(np.int64) # 读取标签数据，数据类型为int64\n",
    "vlist = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\", \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"] # 标签名称列表\n",
    "print('label value:', vlist[label[index]])\n",
    "\n",
    "# 显示图像\n",
    "image = Image.fromarray(image)   # 转换图像格式\n",
    "image.save('./work/out/img.png') # 保存读取图像\n",
    "plt.figure(figsize=(3, 3))       # 设置显示大小\n",
    "plt.imshow(image)                # 设置显示图像\n",
    "plt.show()                       # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n",
      "valid_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n"
     ]
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 训练数据增强\n",
    "def train_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 增强图像\n",
    "    sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "    seq = iaa.Sequential([\n",
    "        sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "        iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "    images = seq(images=images)\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 验证数据增强\n",
    "def valid_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 读取训练数据\n",
    "train_reader = paddle.batch(\n",
    "    paddle.reader.shuffle(paddle.dataset.cifar.train10(), buf_size=50000),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "train_data = next(train_reader()) # 读取训练数据\n",
    "\n",
    "train_image = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取训练图像\n",
    "train_image = train_augment(train_image)                                                       # 训练图像增强\n",
    "train_label = np.array([x[1] for x in train_data]).reshape((-1, 1)).astype(np.int64)           # 读取训练标签\n",
    "print('train_data: image shape {}, label shape:{}'.format(train_image.shape, train_label.shape))\n",
    "\n",
    "# 读取验证数据\n",
    "valid_reader = paddle.batch(\n",
    "    paddle.dataset.cifar.test10(),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "valid_data = next(valid_reader()) # 读取验证数据\n",
    "\n",
    "valid_image = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取验证图像\n",
    "valid_image = valid_augment(valid_image)                                                       # 验证图像增强\n",
    "valid_label = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64)           # 读取验证标签\n",
    "print('valid_data: image shape {}, label shape:{}'.format(valid_image.shape, valid_label.shape))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear, BatchNorm\n",
    "import math\n",
    "\n",
    "# 模组结构：输入维度，输出维度，滑动步长，基础长度, 队列长度\n",
    "group_arch = [(3, 128, 1, 2, 5), (512, 256, 2, 2, 5), (1024, 512, 2, 2, 5)]\n",
    "group_dim  = 2048 # 模组输出维度\n",
    "class_dim  = 10   # 类别数量维度\n",
    "\n",
    "# 卷积单元\n",
    "class ConvUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=3, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化卷积单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ConvUnit, self).__init__()\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=filter_size,\n",
    "            stride=stride,\n",
    "            padding=(filter_size-1)//2,                       # 输出特征图大小不变\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# 投影单元\n",
    "class ProjUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=1, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化投影单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ProjUnit, self).__init__()\n",
    "        \n",
    "        # 添加池化\n",
    "        self.pool = Pool2D(\n",
    "            pool_size=filter_size,\n",
    "            pool_stride=stride,\n",
    "            pool_padding=0,\n",
    "            pool_type='avg')\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=1,\n",
    "            stride=1,\n",
    "            padding=0,\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行池化卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行池化\n",
    "        x = self.pool(x)\n",
    "        \n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# 分割结构\n",
    "class HSBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, splits=5, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始HS-Block结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长，1保持不变，2下采样\n",
    "            splits  - 分割次数\n",
    "            act     - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(HSBlock, self).__init__()\n",
    "        \n",
    "        # 计算通道\n",
    "        channel0 = out_dim // splits\n",
    "        channel1 = channel0 * 2\n",
    "        channel2 = channel0 * splits\n",
    "        \n",
    "        # 特征平分\n",
    "        self.conv1 = ConvUnit(in_dim=in_dim, out_dim=channel2, filter_size=1, stride=1, act=act)\n",
    "        \n",
    "        # 特征升维\n",
    "        self.conv2 = ConvUnit(in_dim=channel0, out_dim=channel1, filter_size=3, stride=1, act=act)\n",
    "        \n",
    "        # 重复合并\n",
    "        self.conv3 = ConvUnit(in_dim=channel1, out_dim=channel1, filter_size=3, stride=1, act=act)\n",
    "        self.conv4 = ConvUnit(in_dim=channel1, out_dim=channel1, filter_size=3, stride=1, act=act)\n",
    "        self.conv5 = ConvUnit(in_dim=channel1, out_dim=channel1, filter_size=3, stride=1, act=act)\n",
    "        \n",
    "        # 合并特征\n",
    "        self.conv6 = ConvUnit(in_dim=channel2 + channel0, out_dim=out_dim, filter_size=1, stride=1, act=act)\n",
    "            \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 特征平分\n",
    "        x = self.conv1(x)\n",
    "        x0, x1, x2, x3, x4 = fluid.layers.split(input=x, num_or_sections=5, dim=1)\n",
    "        \n",
    "        # 特征升维\n",
    "        x1 = self.conv2(x1)\n",
    "        x1_0, x1_1 = fluid.layers.split(input=x1, num_or_sections=2, dim=1)\n",
    "        \n",
    "        # 重复合并\n",
    "        x2 = fluid.layers.concat(input=[x2, x1_1], axis=1)\n",
    "        x2 = self.conv3(x2)\n",
    "        x2_0, x2_1 = fluid.layers.split(input=x2, num_or_sections=2, dim=1)\n",
    "        \n",
    "        x3 = fluid.layers.concat(input=[x3, x2_1], axis=1)\n",
    "        x3 = self.conv4(x3)\n",
    "        x3_0, x3_1 = fluid.layers.split(input=x3, num_or_sections=2, dim=1)\n",
    "        \n",
    "        x4 = fluid.layers.concat(input=[x4, x3_1], axis=1)\n",
    "        x4 = self.conv5(x4)\n",
    "        \n",
    "        # 合并特征\n",
    "        x = fluid.layers.concat(input=[x0, x1_0, x2_0, x3_0, x4], axis=1)\n",
    "        x = self.conv6(x)\n",
    "        \n",
    "        return x\n",
    "    \n",
    "# 基础结构\n",
    "class SSRBasic(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, queues=1, is_pass=True):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化基础结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            queues  - 队列长度\n",
    "            is_pass - 是否直连\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRBasic, self).__init__()\n",
    "        \n",
    "        # 是否直连标识\n",
    "        self.is_pass = is_pass\n",
    "        \n",
    "        # 添加投影路径\n",
    "        self.proj = ProjUnit(in_dim=in_dim, out_dim=out_dim*4, filter_size=stride, stride=stride, act=None)\n",
    "        \n",
    "        # 添加卷积路径\n",
    "        self.con1 = ConvUnit(in_dim=in_dim, out_dim=out_dim, filter_size=1, stride=stride, act='relu')\n",
    "        \n",
    "        if queues==1:\n",
    "            self.con2 = ConvUnit(in_dim=out_dim, out_dim=out_dim, filter_size=3, stride=1, act='relu')\n",
    "        else:\n",
    "            self.con2 = HSBlock(in_dim=out_dim, out_dim=out_dim, stride=1, splits=queues, act='relu')\n",
    "        \n",
    "        self.con3 = ConvUnit(in_dim=out_dim, out_dim=out_dim*4, filter_size=1, stride=1, act=None)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "            y - 输出特征\n",
    "        \"\"\"\n",
    "        # 直连路径\n",
    "        if self.is_pass: # 是否直连\n",
    "            x_pass = x\n",
    "        else:            # 否则投影\n",
    "            x_pass = self.proj(x)\n",
    "        \n",
    "        # 卷积路径\n",
    "        x_con1 = self.con1(x)      # 特征降维\n",
    "        x_con2 = self.con2(x_con1) # 特征提取\n",
    "        x_con3 = self.con3(x_con2) # 特征升维\n",
    "        \n",
    "        # 输出特征\n",
    "        x = fluid.layers.elementwise_add(x=x_pass, y=x_con3, act='relu') # 直连路径与卷积路径进行特征相加\n",
    "        y = x\n",
    "        \n",
    "        return x, y\n",
    "    \n",
    "# 模块结构\n",
    "class SSRBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, basics=1, queues=1):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模块结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            basics  - 基础长度\n",
    "            queues  - 队列长度\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRBlock, self).__init__()\n",
    "        \n",
    "        # 添加模块列表\n",
    "        self.block_list = [] # 模块列表\n",
    "        for i in range(basics):\n",
    "            block_item = self.add_sublayer( # 构造模块项目\n",
    "                'block_' + str(i),\n",
    "                SSRBasic(\n",
    "                    in_dim=(in_dim if i==0 else out_dim*4), # 每组模块项目除第一块外，输入维度=输出维度\n",
    "                    out_dim=out_dim,\n",
    "                    stride=(stride if i==0 else 1), # 每组模块项目除第一块外，stride=1\n",
    "                    queues=queues,\n",
    "                    is_pass=(False if i==0 else True))) # 每组模块项目除第一块外，is_pass=True\n",
    "            self.block_list.append(block_item) # 添加模块项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "            y_list - 输出特征列表\n",
    "        \"\"\"\n",
    "        y_list = [] # 模块输出列表\n",
    "        for block_item in self.block_list:\n",
    "            x, y_item = block_item(x) # 提取模块特征\n",
    "            y_list.append(y_item) # 添加输出列表\n",
    "            \n",
    "        return x, y_list\n",
    "\n",
    "# 模组结构\n",
    "class SSRGroup(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模组结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRGroup, self).__init__()\n",
    "        \n",
    "        # 添加模组列表\n",
    "        self.group_list = [] # 模组列表\n",
    "        for i, block_arch in enumerate(group_arch):\n",
    "            group_item = self.add_sublayer( # 构造模组项目\n",
    "                'group_' + str(i),\n",
    "                SSRBlock(\n",
    "                    in_dim=block_arch[0],\n",
    "                    out_dim=block_arch[1],\n",
    "                    stride=block_arch[2],\n",
    "                    basics=block_arch[3],\n",
    "                    queues=block_arch[4]))\n",
    "            self.group_list.append(group_item) # 添加模组项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "            y_list - 输出特征列表\n",
    "        \"\"\"\n",
    "        y_list = [] # 模组输出列表\n",
    "        for group_item in self.group_list:\n",
    "            x, y_item = group_item(x) # 提取模组特征\n",
    "            y_list.append(y_item) # 添加输出列表\n",
    "            \n",
    "        return x, y_list\n",
    "        \n",
    "# 分割网络\n",
    "class SSRNet(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化分割网络，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRNet, self).__init__()\n",
    "        \n",
    "        # 添加模组结构\n",
    "        self.backbone = SSRGroup() # 输出：N*C*H*W\n",
    "        \n",
    "        # 添加全连接层\n",
    "        self.pool = Pool2D(global_pooling=True, pool_type='avg') # 输出：N*C*1*1\n",
    "        \n",
    "        stdv = 1.0/(math.sqrt(group_dim)*1.0)                    # 设置均匀分布权重方差\n",
    "        self.fc = Linear(                                        # 输出：=N*10\n",
    "            input_dim=group_dim,\n",
    "            output_dim=class_dim,\n",
    "            param_attr=fluid.initializer.Uniform(-stdv, stdv),   # 使用均匀分布初始权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),           # 使用常量数值初始偏置\n",
    "            act='softmax')\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入图像进行分类\n",
    "        输入:\n",
    "            x - 输入图像\n",
    "        输出:\n",
    "            x - 预测结果\n",
    "        \"\"\"\n",
    "        # 提取特征\n",
    "        x, y_list = self.backbone(x)\n",
    "        \n",
    "        # 进行预测\n",
    "        x = self.pool(x)\n",
    "        x = fluid.layers.reshape(x, [x.shape[0], -1])\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tatol param: 17964514\n",
      "infer shape: [1, 10]\n"
     ]
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.base import to_variable\n",
    "import numpy as np\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 输入数据\n",
    "    x = np.random.randn(1, 3, 32, 32).astype(np.float32)\n",
    "    x = to_variable(x)\n",
    "    \n",
    "    # 进行预测\n",
    "    backbone = SSRNet() # 设置网络\n",
    "    \n",
    "    infer = backbone(x) # 进行预测\n",
    "    \n",
    "    # 显示结果\n",
    "    parameters = 0\n",
    "    for p in backbone.parameters():\n",
    "        parameters += np.prod(p.shape) # 统计参数\n",
    "    \n",
    "    print('tatol param:', parameters)\n",
    "    print('infer shape:', infer.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "complete - train time: 24978s, best epoch: 277, best loss: 0.213338, best accuracy: 95.19%\r"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.utils.plot import Ploter\n",
    "import numpy as np\n",
    "import time\n",
    "import math\n",
    "import os\n",
    "\n",
    "epoch_num = 300   # 训练周期，取值一般为[1,300]\n",
    "train_batch = 128 # 训练批次，取值一般为[1,256]\n",
    "valid_batch = 128 # 验证批次，取值一般为[1,256]\n",
    "displays = 100    # 显示迭代\n",
    "\n",
    "start_lr = 0.00001                         # 开始学习率，取值一般为[1e-8,5e-1]\n",
    "based_lr = 0.1                             # 基础学习率，取值一般为[1e-8,5e-1]\n",
    "epoch_iters = math.ceil(50000/train_batch) # 每轮迭代数\n",
    "warmup_iter = 10 * epoch_iters             # 预热迭代数，取值一般为[1,10]\n",
    "\n",
    "momentum = 0.9     # 优化器动量\n",
    "l2_decay = 0.00005 # 正则化系数，取值一般为[1e-5,5e-4]\n",
    "epsilon = 0.05     # 标签平滑率，取值一般为[1e-2,1e-1]\n",
    "\n",
    "checkpoint = False                   # 断点标识\n",
    "model_path = './work/out/hs-resnet'  # 模型路径\n",
    "result_txt = './work/out/result.txt' # 结果文件\n",
    "class_num  = 10                      # 类别数量\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 准备数据\n",
    "    train_reader = paddle.batch(\n",
    "        reader=paddle.reader.shuffle(reader=paddle.dataset.cifar.train10(), buf_size=50000),\n",
    "        batch_size=train_batch)\n",
    "    \n",
    "    valid_reader = paddle.batch(\n",
    "        reader=paddle.dataset.cifar.test10(),\n",
    "        batch_size=valid_batch)\n",
    "    \n",
    "    # 声明模型\n",
    "    model = SSRNet()\n",
    "    \n",
    "    # 优化算法\n",
    "    consine_lr = fluid.layers.cosine_decay(based_lr, epoch_iters, epoch_num) # 余弦衰减策略\n",
    "    decayed_lr = fluid.layers.linear_lr_warmup(consine_lr, warmup_iter, start_lr, based_lr) # 线性预热策略\n",
    "    \n",
    "    optimizer = fluid.optimizer.Momentum(\n",
    "        learning_rate=decayed_lr,                           # 衰减学习策略\n",
    "        momentum=momentum,                                  # 优化动量系数\n",
    "        regularization=fluid.regularizer.L2Decay(l2_decay), # 正则衰减系数\n",
    "        parameter_list=model.parameters())\n",
    "    \n",
    "    # 加载断点\n",
    "    if checkpoint: # 是否加载断点文件\n",
    "        model_dict, optimizer_dict = fluid.load_dygraph(model_path) # 加载断点参数\n",
    "        model.set_dict(model_dict)                                  # 设置权重参数\n",
    "        optimizer.set_dict(optimizer_dict)                          # 设置优化参数\n",
    "    else:          # 否则删除结果文件\n",
    "        if os.path.exists(result_txt): # 如果存在结果文件\n",
    "            os.remove(result_txt)      # 那么删除结果文件\n",
    "    \n",
    "    # 初始训练\n",
    "    avg_train_loss = 0 # 平均训练损失\n",
    "    avg_valid_loss = 0 # 平均验证损失\n",
    "    avg_valid_accu = 0 # 平均验证精度\n",
    "    \n",
    "    iterator = 1                                # 迭代次数\n",
    "    train_prompt = \"Train loss\"                 # 训练标签\n",
    "    valid_prompt = \"Valid loss\"                 # 验证标签\n",
    "    ploter = Ploter(train_prompt, valid_prompt) # 训练图像\n",
    "    \n",
    "    best_epoch = 0           # 最好周期\n",
    "    best_accu = 0            # 最好精度\n",
    "    best_loss = 100.0        # 最好损失\n",
    "    train_time = time.time() # 训练时间\n",
    "    \n",
    "    # 开始训练\n",
    "    for epoch_id in range(epoch_num):\n",
    "        # 训练模型\n",
    "        model.train() # 设置训练\n",
    "        for batch_id, train_data in enumerate(train_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = train_augment(image_data)                                                        # 使用数据增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "\n",
    "            label_data = np.array([x[1] for x in train_data]).astype(np.int64)                        # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                             # 转换数据类型\n",
    "            label = fluid.layers.label_smooth(label=fluid.one_hot(label, class_num), epsilon=epsilon) # 使用标签平滑\n",
    "            label.stop_gradient = True                                                                # 停止梯度传播\n",
    "\n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label, soft_label=True)\n",
    "            train_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            # 反向传播\n",
    "            train_loss.backward()\n",
    "            optimizer.minimize(train_loss)\n",
    "            model.clear_gradients()\n",
    "            \n",
    "            # 显示结果\n",
    "            if iterator % displays == 0:\n",
    "                # 显示图像\n",
    "                avg_train_loss = train_loss.numpy()[0]                # 设置训练损失\n",
    "                ploter.append(train_prompt, iterator, avg_train_loss) # 添加训练图像\n",
    "                ploter.plot()                                         # 显示训练图像\n",
    "                \n",
    "                # 打印结果\n",
    "                print(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\".format(\n",
    "                    iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "                \n",
    "                # 写入文件\n",
    "                with open(result_txt, 'a') as file:\n",
    "                    file.write(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\\n\".format(\n",
    "                        iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "            \n",
    "            # 增加迭代\n",
    "            iterator += 1\n",
    "            \n",
    "        # 验证模型\n",
    "        valid_loss_list = [] # 验证损失列表\n",
    "        valid_accu_list = [] # 验证精度列表\n",
    "        \n",
    "        model.eval()   # 设置验证\n",
    "        for batch_id, valid_data in enumerate(valid_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = valid_augment(image_data)                                                        # 使用图像增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "            \n",
    "            label_data = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64) # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                       # 转换数据类型\n",
    "            label.stop_gradient = True                                                          # 停止梯度传播\n",
    "            \n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算精度\n",
    "            valid_accu = fluid.layers.accuracy(infer,label)\n",
    "            \n",
    "            valid_accu_list.append(valid_accu.numpy())\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label)\n",
    "            valid_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            valid_loss_list.append(valid_loss.numpy())\n",
    "        \n",
    "        # 设置结果\n",
    "        avg_valid_accu = np.mean(valid_accu_list)             # 设置验证精度\n",
    "        \n",
    "        avg_valid_loss = np.mean(valid_loss_list)             # 设置验证损失\n",
    "        ploter.append(valid_prompt, iterator, avg_valid_loss) # 添加训练图像\n",
    "        \n",
    "        # 保存模型\n",
    "        fluid.save_dygraph(model.state_dict(), model_path)     # 保存权重参数\n",
    "        fluid.save_dygraph(optimizer.state_dict(), model_path) # 保存优化参数\n",
    "        \n",
    "        if avg_valid_loss < best_loss:\n",
    "            fluid.save_dygraph(model.state_dict(), model_path + '-best') # 保存权重\n",
    "            \n",
    "            best_epoch = epoch_id + 1                                    # 更新迭代\n",
    "            best_accu = avg_valid_accu                                   # 更新精度\n",
    "            best_loss = avg_valid_loss                                   # 更新损失\n",
    "    \n",
    "    # 显示结果\n",
    "    train_time = time.time() - train_time # 设置训练时间\n",
    "    print('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}'.format(\n",
    "        train_time, best_epoch, best_loss, best_accu))\n",
    "    \n",
    "    # 写入文件\n",
    "    with open(result_txt, 'a') as file:\n",
    "        file.write('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}\\n'.format(\n",
    "            train_time, best_epoch, best_loss, best_accu))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "infer time: 0.021413s, infer value: horse\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import time\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "image_path = './work/out/img.png' # 图片路径\n",
    "model_path = './work/out/hs-resnet-best' # 模型路径\n",
    "\n",
    "# 加载图像\n",
    "def load_image(image_path):\n",
    "    \"\"\"\n",
    "    功能:\n",
    "        读取图像并转换到输入格式\n",
    "    输入:\n",
    "        image_path - 输入图像路径\n",
    "    输出:\n",
    "        image - 输出图像\n",
    "    \"\"\"\n",
    "    # 读取图像\n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    \n",
    "    # 转换格式\n",
    "    image = image.resize((32, 32), Image.ANTIALIAS) # 调整图像大小\n",
    "    image = np.array(image, dtype=np.float32) # 转换数据格式，数据类型转换为float32\n",
    "\n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    image = (image/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    image = image.transpose((2, 0, 1)).astype(np.float32) # 数据格式从HWC转换为CHW，数据类型转换为float32\n",
    "    \n",
    "    # 增加维度\n",
    "    image = np.expand_dims(image, axis=0) # 增加数据维度\n",
    "    \n",
    "    return image\n",
    "\n",
    "# 预测图像\n",
    "with fluid.dygraph.guard():\n",
    "    # 读取图像\n",
    "    image = load_image(image_path)\n",
    "    image = fluid.dygraph.to_variable(image)\n",
    "    \n",
    "    # 加载模型\n",
    "    model = SSRNet()                               # 加载模型\n",
    "    model_dict, _ = fluid.load_dygraph(model_path) # 加载权重\n",
    "    model.set_dict(model_dict)                     # 设置权重\n",
    "    model.eval()                                   # 设置验证\n",
    "    \n",
    "    # 前向传播\n",
    "    infer_time = time.time()              # 推断开始时间\n",
    "    infer = model(image)\n",
    "    infer_time = time.time() - infer_time # 推断结束时间\n",
    "    \n",
    "    # 显示结果\n",
    "    vlist = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\", \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"] # 标签名称列表\n",
    "    print('infer time: {:f}s, infer value: {}'.format(infer_time, vlist[np.argmax(infer.numpy())]) )\n",
    "    \n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    plt.figure(figsize=(3, 3))     # 设置显示大小\n",
    "    plt.imshow(image)              # 设置显示图像\n",
    "    plt.show()                     # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
